import React, { memo, useState, useMemo, useCallback, useRef, useEffect } from "react"
const CompareResult = {
    eq: 1,
    It: 2,
    gt: 3
}
const binarySearch = (list, value, compareFunc) => {
    let start = 0
    let end = list.length - 1
    let tempIndex = null
    while (start <= end) {
        tempIndex = Math.floor((end + start) / 2)
        const midValue = list[tempIndex]
        const compareRes = compareFunc(midValue, value)
        if (compareRes === CompareResult.eq) {
            return tempIndex
        }
        if (compareRes === CompareResult.It) {
            start = tempIndex + 1
        } else if (compareRes === CompareResult.gt) {
            end = tempIndex - 1
        }
    }
    return tempIndex
}

const Container = memo((props) => {
    const { height = 500, onScroll, eleRef } = props
    return <div ref={eleRef} style={{ overflowY: 'auto', overflowX: 'hidden', height: height + 'px' }} onScroll={onScroll}>
        {props.children}
    </div>
})

const ListBox = memo((props) => {
    const { style = {}, id } = props
    return <div id={id} style={{ background: 'pink', position: "relative", ...style }}>
        {props.children}
    </div>
})

const WrapBox = memo((props) => {
    const { eleRef } = props
    return <div ref={eleRef}></div>
})
/* item 高度固定 */
const VirList = memo(({ list = [], containerHeight = 800, ItemBox = <></>, itemHeight = 50, ...props }) => {
    const ContainerRef = useRef()
    const [startIndex, setStartIndex] = useState(0)
    const wrapHeight = useMemo(() => {
        return list.length * itemHeight
    }, [list, itemHeight])
    //  区域最多显示多少条
    const limit = useMemo(() => {
        return Math.ceil(containerHeight / itemHeight)
    }, [startIndex])
    // 当前区域显示的列表的结束索引
    const endIndex = useMemo(() => {
        return Math.min(startIndex + limit, list.length - 1)
    }, [startIndex, limit])

    const renderList = useCallback(() => {
        const rows = []
        for (let i = startIndex; i <= endIndex; i++) {
            // 渲染列表
            rows.push(<ItemBox
                data={i}
                key={i}
                style={{
                    width: '100%',
                    height: itemHeight - 1 + 'px',
                    borderBottom: '1px solid #aaa',
                    position: "absolute",
                    top: i * itemHeight + 'px',
                    left: 0,
                    right: 0
                }}
            />)
        }
        return rows
    }, [startIndex, endIndex, ItemBox])
    const handleScroll = useCallback((e) => {
        if (e.target !== ContainerRef.current) return
        const scrollTop = e.target.scrollTop
        const currentIndex = Math.floor(scrollTop / itemHeight)
        if (currentIndex !== startIndex) {
            setStartIndex(currentIndex)
        }
    }, [Container, itemHeight, startIndex])

    return (
        <Container height={containerHeight} eleRef={ContainerRef} onScroll={handleScroll}>
            <ListBox style={{ height: wrapHeight + 'px' }}>
                {renderList()}
            </ListBox>
        </Container>
    )
})

/* item 高度不固定 */
const VirList1 = memo((props) => {
    const { list = [], containerHeight = 800, ItemBox = <></>, estimateItemHeight = 90, ...otherProps } = props
    const ContainerRef = useRef()
    const WraperRef = useRef()
    const [startIndex, setStartIndex] = useState(0)
    const [scrollTop, setScrollTop] = useState(0)

    const [positionCache, setPositionCache] = useState(() => {
        const positionList = []
        list.forEach((_, i) => {
            positionList[i] = {
                index: i,
                height: estimateItemHeight,
                top: i * estimateItemHeight,
                bottom: (i + 1) * estimateItemHeight // ?
            }
        })
        return positionList
    })
    /* 只计算开始的几个 */
    const limit = useMemo(() => {
        let sum = 0
        let i = 0
        for (; i < positionCache.length; i++) {
            sum += positionCache[i].height
            if (sum >= containerHeight) break
        }
        return i
    }, [positionCache])
    const endeInex = useMemo(() => {
        return Math.min(startIndex + limit, list.length - 1)
    }, [startIndex, limit])

    const warpHeight = useMemo(() => {
        let len = positionCache.length
        if (len !== 0) {
            return positionCache[len - 1].bottom
        }
        return list.length * estimateItemHeight
    }, [list, positionCache, estimateItemHeight])

    useEffect(() => {
        if (!WraperRef.current) return
        const nodeList = WraperRef.current.childNodes
        const positList = [...positionCache]
        let needUpdate = false
        nodeList.forEach((node, i) => {
            let newHeight = node.getBoundingClientRect().height
            const nodeID = Number(node.id.split('-')[1])
            const oldHeight = positionCache[nodeID].height
            const dValue = oldHeight - newHeight
            if (dValue) {
                needUpdate = true
                positList[nodeID].height = node.getBoundingClientRect().height
                positList[nodeID].bottom = nodeID > 0 ? (positList[nodeID - 1].bottom + positList[nodeID].height) : positList[nodeID].height
                positList[nodeID].top = nodeID > 0 ? positList[nodeID - 1].bottom : 0
                for (let j = nodeID + 1, len = positList.length; j < len; j++) {
                    positList[j].top = positList[j - 1].bottom
                    positList[j].bottom += dValue
                }
            }
            if (needUpdate) {
                setPositionCache(positList)
            }
        })
    }, [scrollTop])
    const getStartIndex = (scrollTop) => {
        let idx = binarySearch(positionCache, scrollTop, (currentValue, targetValue) => {
            const currentCompareValue = currentValue.bottom
            if (currentCompareValue === targetValue) {
                return CompareResult.eq
            }
            if (currentCompareValue < targetValue) {
                return CompareResult.It
            }
            return CompareResult.gt
        })
        const targetItem = positionCache[idx]
        if (targetItem.bottom < scrollTop) {
            idx += 1
        }
        return idx
    }
    const getTransfrom = useCallback(function () {
        return `translate3d(0,${startIndex >= 1 ? positionCache[startIndex - 1].bottom : 0}px,0)`
    }, [positionCache, startIndex])
    const handleScroll = useCallback(e => {
        if (e.target !== ContainerRef.current) return
        const scrollTop = e.target.scrollTop
        setScrollTop(scrollTop)
        const currentStartIndex = getStartIndex(scrollTop)
        if (currentStartIndex !== startIndex) {
            setStartIndex(currentStartIndex)
            // console.log('startIndex :>> ' + startIndex, 'limit :>> ' + limit, 'endeInex :>> ' + endeInex);
        }
    }, [ContainerRef, estimateItemHeight, startIndex])
    const renderList = useCallback(() => {
        const rows = []
        for (let i = startIndex; i <= endeInex; i++) {
            rows.push(<ItemBox
                data={list[i]}
                index={i}
                key={i}
                style={{ width: '100%', borderBottom: '1px solid #aaa' }}
            />)
        }
        return rows
    }, [startIndex, endeInex, ItemBox])
    return (
        <Container
            onScroll={handleScroll}
            height={containerHeight}
            eleRef={ContainerRef}
        >
            <ListBox style={{ height: warpHeight + 'px' }}>
                <div style={{ transform: getTransfrom() }} ref={WraperRef}>
                    {renderList()}
                </div>
            </ListBox>
        </Container>
    )
})
export default VirList1